Skip to content

fix: session isolation, Gemini tool-call correlation, compression failure hardening#45

Merged
MyPrototypeWhat merged 1 commit into
mainfrom
claude/strange-franklin-2aee2a
Jul 13, 2026
Merged

fix: session isolation, Gemini tool-call correlation, compression failure hardening#45
MyPrototypeWhat merged 1 commit into
mainfrom
claude/strange-franklin-2aee2a

Conversation

@MyPrototypeWhat

Copy link
Copy Markdown
Owner

Summary

Fixes the confirmed findings from a full architecture review of all three packages, developed test-first (10+ new tests written red-first).

P0 — correctness

  • Middleware session isolation: a middleware created at module scope previously shared one Janitor across every conversation it served — token-usage feeds, E10 compression suppression, and circuit-breaker counts leaked between users. Janitor state is now pooled per session (providerOptions: { contextChef: { sessionId } } in the AI SDK middleware, ctx.conversationId in TanStack). Calls without a session identity share the default slot (prior behavior, zero breaking change). New maxSessions cap (default 256, LRU, validated).
  • Gemini tool-call correlation: fromGemini's correlation state was scoped to a single message, so functionResponses arriving in a later content entry always fell back to the -0 ID — repeat calls of the same tool produced duplicate IDs and misattributed results. Now per-name counters + FIFO queues span the conversation.

P1 — failure-path hardening

  • Compression-model failures no longer inject the raw error text into the LLM-bound fallback summary (goes to logger instead).
  • onCompress and onBeforeCompress now share one degradation stance: a throwing hook is caught and logged, never fails compile() (JSDoc contracts updated).
  • Invalid session keys (empty string / non-string / malformed namespace) warn once and route to the default slot — semantics unified across both middleware packages.
  • Janitor missing-config nags fire once per middleware instead of once per session.
  • SessionPool rejects non-positive maxSize (would silently disable pooling).

Cleanup

  • Non-text tool-result parts flatten to typed placeholders ([tool result file: image/png (chart.png)]) instead of being silently dropped; the truncator's duplicate extraction delegates to the same implementation.
  • Shared role-flattening extracted to core as flattenForCompression (both middlewares reuse it; ~120 lines of copy-paste removed).
  • OpenAIAdapter.compile drops the per-message JSON.parse(JSON.stringify(...)) round-trip for a direct clone (honors toJSON, so Date still lands as ISO string).
  • Placeholder vocabulary centralized in Prompts; peerDependencies tightened to ^4.0.0 / ^7.0.0.

Changesets included for all three packages (core minor, ai-sdk-middleware minor, tanstack-ai minor).

Test plan

  • 925 tests pass (core 733, ai-sdk-middleware 123, tanstack-ai 69) — includes new red-first tests for every behavioral fix
  • pnpm -r run typecheck clean
  • biome check . clean
  • pnpm -r run build clean

…lure hardening

Fixes from a full architecture review of the three packages:

core:
- fromGemini correlates functionResponses via per-name FIFO queues spanning
  the conversation; repeat calls of the same tool no longer produce duplicate
  IDs or misattributed results
- compression-model failures no longer inject the raw error into the
  LLM-bound fallback summary (logged via `logger` instead)
- onCompress and onBeforeCompress share one degradation stance: a throwing
  hook is caught and logged, never fails compile()
- new SessionPool (LRU, validated maxSize) + normalizeSessionKey /
  DEFAULT_SESSION_KEY / dedupeConstructionWarnings
- new flattenForCompression export — canonical role-flattening for the
  summarizeHistory compress-callback contract
- OpenAIAdapter.compile clones without the JSON round-trip (honors toJSON)
- placeholder vocabulary centralized in Prompts

ai-sdk-middleware / tanstack-ai:
- Janitor state is now per-session (providerOptions.contextChef.sessionId /
  ctx.conversationId) instead of shared across every conversation the
  middleware serves; invalid session keys warn once and route to the
  default slot with unified semantics across both packages
- construction-time config nags fire once per middleware, not per session
- non-text tool-result parts flatten to typed placeholders instead of
  being silently dropped
- compression flattening reuses core's flattenForCompression
- peerDependencies tightened to ^4.0.0 / ^7.0.0 (ai-sdk-middleware)
@MyPrototypeWhat
MyPrototypeWhat merged commit d0ac644 into main Jul 13, 2026
2 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant